home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 42 / Amiga Format AFCD42 (Issue 126, Aug 1999).iso / -serious- / comms / other / slrn / slrn_src / src / nntplib.c < prev    next >
C/C++ Source or Header  |  1999-05-14  |  21KB  |  1,019 lines

  1. /* Copyright (c) 1998 John E. Davis (davis@space.mit.edu)
  2.  *
  3.  * This file is part of slrn.
  4.  *
  5.  * Slrn is free software; you can redistribute it and/or modify it
  6.  * under the terms of the GNU General Public License as published by the
  7.  * Free Software Foundation; either version 2, or (at your option) any
  8.  * later version.
  9.  * 
  10.  * Slrn is distributed in the hope that it will be useful, but WITHOUT
  11.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13.  * for more details.
  14.  * 
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with Slrn; see the file COPYING.  If not, write to the Free
  17.  * Software Foundation, 59 Temple Place - Suite 330, 
  18.  * Boston, MA  02111-1307, USA.
  19.  */
  20.  
  21. #include "config.h"
  22. #include <stdio.h>
  23. #include <string.h>
  24.  
  25. #ifdef HAVE_STDLIB_H
  26. # include <stdlib.h>
  27. #endif
  28.  
  29. #ifdef HAVE_UNISTD_H
  30. # include <unistd.h>
  31. #endif
  32.  
  33. #include <stdarg.h>
  34. #include <slang.h>
  35. #include "jdmacros.h"
  36.  
  37. #include "sltcp.h"
  38.  
  39. #include "nntpcodes.h"
  40. #include "nntplib.h"
  41. #include "util.h"
  42. #include "ttymsg.h"
  43.  
  44. void (*NNTP_Connection_Lost_Hook) (NNTP_Type *);
  45. int (*NNTP_Authorization_Hook) (char *, char **, char **);
  46.  
  47. FILE *NNTP_Debug_Fp;
  48.  
  49. static int _nntp_connect_server (NNTP_Type *);
  50.  
  51. static NNTP_Type *nntp_allocate_nntp (void)
  52. {
  53.    static NNTP_Type nn;
  54.    NNTP_Type *s;
  55.  
  56.    s = &nn;
  57.  
  58.    memset ((char *) s, 0, sizeof (NNTP_Type));
  59.    s->can_xover = -1;
  60.    s->can_xhdr = -1;
  61.    s->can_xpat = -1;
  62.    s->can_xgtitle = -1;
  63.  
  64.    return s;
  65. }
  66.  
  67. static void _nntp_deallocate_nntp (NNTP_Type *s)
  68. {
  69.    if (s == NULL) return;
  70.  
  71.    memset ((char *) s, 0, sizeof (NNTP_Type));
  72. }
  73.  
  74. int nntp_fgets_server (NNTP_Type *s, char *buf, unsigned int len)
  75. {
  76.    if ((s == NULL) || (s->init_state <= 0))
  77.      return -1;
  78.  
  79.    *buf = 0;
  80.  
  81.    if (-1 == sltcp_fgets (s->tcp, buf, len))
  82.      {
  83.     (void) nntp_disconnect_server (s);
  84.     return -1;
  85.      }
  86.  
  87.    if (NNTP_Debug_Fp != NULL)
  88.      fprintf (NNTP_Debug_Fp, "<%s", buf);
  89.  
  90.    return 0;
  91. }
  92.  
  93. int nntp_fputs_server (NNTP_Type *s, char *buf)
  94. {
  95.    if ((s == NULL) || (s->init_state == 0))
  96.      return -1;
  97.  
  98.    if (NNTP_Debug_Fp != NULL)
  99.      fprintf (NNTP_Debug_Fp, ">%s\n", buf);
  100.  
  101.    if (-1 == sltcp_fputs (s->tcp, buf))
  102.      {
  103.     (void) nntp_disconnect_server (s);
  104.     return -1;
  105.      }
  106.  
  107.    return 0;
  108. }
  109.  
  110. int nntp_write_server (NNTP_Type *s, char *buf, unsigned int n)
  111. {
  112.    if ((s == NULL) || (s->init_state == 0))
  113.      return -1;
  114.  
  115.    if (NNTP_Debug_Fp != NULL)
  116.      {
  117.     unsigned int i;
  118.     fputc ('>', NNTP_Debug_Fp);
  119.     for (i = 0; i < n; i++)
  120.       fputc (buf[i], NNTP_Debug_Fp);
  121.     fputc ('\n', NNTP_Debug_Fp);
  122.      }
  123.  
  124.    if (n != sltcp_write (s->tcp, buf, n))
  125.      {
  126.     (void) nntp_disconnect_server (s);
  127.     return -1;
  128.      }
  129.  
  130.    return 0;
  131. }
  132.  
  133. int nntp_gets_server (NNTP_Type *s, char *buf, unsigned int len)
  134. {
  135.    if (-1 == nntp_fgets_server (s, buf, len))
  136.      return -1;
  137.  
  138.    len = strlen (buf);
  139.    if (len && (buf[len - 1] == '\n'))
  140.      {
  141.     len--;
  142.     buf[len] = 0;
  143.     if (len && (buf[len - 1] == '\r'))
  144.       buf[len - 1] = 0;
  145.      }
  146.  
  147.    return 0;
  148. }
  149.  
  150. int nntp_puts_server (NNTP_Type *s, char *buf)
  151. {
  152.    if ((-1 == nntp_fputs_server (s, buf))
  153.        || (-1 == nntp_fputs_server (s, "\r\n"))
  154.        || (-1 == sltcp_flush_output (s->tcp)))
  155.      {
  156.     nntp_disconnect_server (s);
  157.     return -1;
  158.      }
  159.  
  160.    return 0;
  161. }
  162.  
  163. static void _nntp_error_response (NNTP_Type *s, char *fmt)
  164. {
  165.    slrn_error ("%s", fmt);
  166.    slrn_error ("Reason: %s", s->rspbuf);
  167. }
  168.  
  169. static int _nntp_try_parse_timeout (char *str)
  170. {
  171.    /* I know of only two timeout responses:
  172.     * 503 Timeout
  173.     * 503 connection timed out
  174.     *
  175.     * Here the idea is to look for 'time' and then 'out'.
  176.     */
  177.    static SLRegexp_Type re;
  178.    unsigned char compiled_pattern_buf[256];
  179.  
  180.    re.pat = (unsigned char *) "time.*out";
  181.    re.buf = compiled_pattern_buf;
  182.    re.case_sensitive = 0;
  183.    re.buf_len = sizeof (compiled_pattern_buf);
  184.  
  185.    (void) SLang_regexp_compile (&re);
  186.    if (NULL == SLang_regexp_match ((unsigned char *) str, strlen (str), &re))
  187.      return -1;
  188.  
  189.    return 0;
  190. }
  191.  
  192. int nntp_get_server_response (NNTP_Type *s)
  193. {
  194.    int status;
  195.  
  196.    if ((s == NULL) || (s->init_state <= 0))
  197.      return -1;
  198.  
  199.    if (-1 == nntp_gets_server (s, s->rspbuf, NNTP_RSPBUF_SIZE))
  200.      return -1;
  201.  
  202.    status = atoi (s->rspbuf);
  203.  
  204.    if (((status == ERR_FAULT) && (-1 != _nntp_try_parse_timeout (s->rspbuf)))
  205.        || (status == ERR_GOODBYE))
  206.      {
  207.     nntp_disconnect_server (s);
  208.     status = -1;
  209.      }
  210.  
  211.    s->code = status;
  212.    return status;
  213. }
  214.  
  215. static int _nntp_reconnect (NNTP_Type *s)
  216. {
  217.    nntp_disconnect_server (s);
  218.    if (0 == (s->flags & NNTP_RECONNECT_OK))
  219.      {
  220.     if (NNTP_Connection_Lost_Hook != NULL)
  221.       (*NNTP_Connection_Lost_Hook) (s);
  222.     return -1;
  223.      }
  224.  
  225.    s->flags &= ~NNTP_RECONNECT_OK;
  226.  
  227.    slrn_message_now ("Server %s dropped connection.  Reconnecting...", s->host);
  228.  
  229.    if (-1 == _nntp_connect_server (s))
  230.      {
  231.     if (NNTP_Connection_Lost_Hook != NULL)
  232.       (*NNTP_Connection_Lost_Hook) (s);
  233.  
  234.     s->flags |= NNTP_RECONNECT_OK;
  235.     return -1;
  236.      }
  237.  
  238.    if ((s->group_name[0] != 0)
  239.        && (-1 == nntp_server_vcmd (s, "GROUP %s", s->group_name)))
  240.      {
  241.     if (NNTP_Connection_Lost_Hook != NULL)
  242.       (*NNTP_Connection_Lost_Hook) (s);
  243.  
  244.     s->flags |= NNTP_RECONNECT_OK;
  245.     return -1;
  246.      }
  247.  
  248.    s->flags |= NNTP_RECONNECT_OK;
  249.    return 0;
  250. }
  251.  
  252. int nntp_start_server_cmd (NNTP_Type *s, char *cmd)
  253. {
  254.    int max_tries = 3;
  255.  
  256.    if (s == NULL)
  257.      return -1;
  258.  
  259.    while ((s->init_state != 0) && max_tries
  260.       && (SLKeyBoard_Quit == 0))
  261.      {
  262.     if (-1 != nntp_puts_server (s, cmd))
  263.       return 0;
  264.  
  265.     if (-1 == _nntp_reconnect (s))
  266.       return -1;
  267.  
  268.     max_tries--;
  269.      }
  270.  
  271.    return -1;
  272. }
  273.  
  274. int nntp_server_cmd (NNTP_Type *s, char *cmd)
  275. {
  276.    int max_tries = 3;
  277.  
  278.    do
  279.      {
  280.     int code;
  281.  
  282.     if (-1 == nntp_start_server_cmd (s, cmd))
  283.       return -1;
  284.  
  285.     if (-1 != (code = nntp_get_server_response (s)))
  286.       return code;
  287.  
  288.     max_tries--;
  289.      }
  290.    while (max_tries && (s->flags & NNTP_RECONNECT_OK));
  291.  
  292.    return -1;
  293. }
  294.  
  295. int nntp_server_vcmd (NNTP_Type *s, char *fmt, ...)
  296. {
  297.    char buf [NNTP_MAX_CMD_LEN];
  298.    va_list ap;
  299.  
  300.    va_start (ap, fmt);
  301.    vsprintf (buf, fmt, ap);
  302.    va_end (ap);
  303.  
  304.    return nntp_server_cmd (s, buf);
  305. }
  306.  
  307. int nntp_start_server_vcmd (NNTP_Type *s, char *fmt, ...)
  308. {
  309.    char buf [NNTP_MAX_CMD_LEN];
  310.    va_list ap;
  311.  
  312.    va_start (ap, fmt);
  313.    vsprintf (buf, fmt, ap);
  314.    va_end (ap);
  315.  
  316.    return nntp_start_server_cmd (s, buf);
  317. }
  318.  
  319. int nntp_close_server (NNTP_Type *s)
  320. {
  321.    if (s == NULL)
  322.      return -1;
  323.  
  324.    if (s->init_state <= 0)
  325.      return 0;
  326.  
  327.    s->init_state = -1;               /* closing */
  328.  
  329.    /* This might be called from a connect_lost hook.  We also do not
  330.     * want to reconnect to send the QUIT command so do not call any
  331.     * of the *server_cmd functions.
  332.     */
  333.    (void) nntp_puts_server (s, "QUIT");
  334.    (void) sltcp_close (s->tcp);
  335.  
  336.    s->tcp = NULL;
  337.    s->init_state = 0;
  338.  
  339.    _nntp_deallocate_nntp (s);
  340.  
  341.    return 0;
  342. }
  343.  
  344. static int _nntp_authorization (NNTP_Type *s)
  345. {
  346.    char *name, *pass;
  347.  
  348.    if (NNTP_Authorization_Hook == NULL)
  349.      return 0;
  350.  
  351.    if (-1 == (*NNTP_Authorization_Hook) (s->host, &name, &pass))
  352.      return -1;
  353.  
  354.    if ((name == NULL) || (pass == NULL))
  355.      return 0;
  356.  
  357.    slrn_message_now ("Authenticating %s ...", name);
  358.  
  359.    if (-1 == nntp_server_vcmd (s, "AUTHINFO USER %s", name))
  360.      return -1;
  361.  
  362.    if (s->code != NEED_AUTHDATA)
  363.      return 0;
  364.  
  365.    switch (nntp_server_vcmd (s, "AUTHINFO PASS %s", pass))
  366.      {
  367.       case -1:
  368.     return -1;
  369.       case ERR_ACCESS:
  370.     _nntp_error_response (s, "Authorization failed.");
  371.     return -1;
  372.       case OK_AUTH:
  373.     s->can_post = 1;
  374.     break;
  375.      }
  376.    return 0;
  377. }
  378.  
  379. static int _nntp_connect_server (NNTP_Type *s)
  380. {
  381.    if (s->tcp != NULL)
  382.      sltcp_close (s->tcp);
  383.  
  384.    slrn_message_now ("Connecting to host %s ...", s->host);
  385.  
  386.    if (NULL == (s->tcp = sltcp_open_connection (s->host, s->port)))
  387.      return -1;
  388.  
  389.    s->init_state = 1;
  390.  
  391.    /* Read logon message. */
  392.    switch (nntp_get_server_response (s))
  393.      {
  394.       case OK_CANPOST:
  395.     s->can_post = 1;
  396.     break;
  397.  
  398.       case OK_NOPOST:
  399.     s->can_post = 0;
  400.     break;
  401.  
  402.       default:
  403.     goto failed;
  404.      }
  405.  
  406.    if ((-1 == nntp_server_cmd (s, "MODE READER"))
  407.        || (ERR_ACCESS == s->code)
  408.        || (-1 == _nntp_authorization (s)))
  409.      goto failed;
  410.  
  411.